#include "dos.h"
#include "mpx_supt.h"
#include "pqueue.h"
#include "R2.h"
#include "R3.h"
#include "R4.h"

int delete_data(void *);

extern pq ready, blocked, suspended, suspended_blocked;

void Load (int argc, char **argv) {
	int priority = atoi(argv[1]);
	char *filename = argv[0];
	int processState = APPLICATION; //change later...
	Load_Program(priority, filename, processState);
}

void Load_Program(int priority, char *filename, int processState) {
	int errorState;
	int progLenP;
	int startOffsetP;
	
	argc = argc;
	
	if(priority < -128 || priority > 127){
		printf(
			"\n\tError: The priority must be at least -128 and at most 127.\n\n"
		);
		return;
	}
	if(Find_PCB(filename) != NULL){
		printf(
			"\n\tError: The process name is not unique.\n\n"
		);
		return;
	}

	if((errorState = sys_check_program("\0", filename, &progLenP, &startOffsetP)) == 0){
		ppcb newPCB;
		context *npc;
		int loadError;
		
		newPCB = Setup_PCB(filename, processState, priority);
		newPCB->load = (unsigned char *)sys_alloc_mem(progLenP); 
		newPCB->execute = newPCB->load + startOffsetP;
		newPCB->memory_size = progLenP;
		
		npc = (context*) newPCB->stack_top;
		npc->IP = FP_OFF(newPCB->execute); 
		npc->CS = FP_SEG(newPCB->execute);
		npc->FLAGS = 0x200;
		npc->DS = _DS;
		npc->ES = _ES;
		
		if((loadError = sys_load_program((void *)newPCB->load, newPCB->memory_size, "\0", filename)) == 0){
			Insert_PCB(SUSPENDED, newPCB);
		}
		else if(loadError == ERR_SUP_NAMLNG){
			printf(
				"\n\tError: The pathname is too long.\n\n"
			);
			return;
		}
		else if(loadError == ERR_SUP_FILNFD){
			printf(
				"\n\tError: File not found.\n\n"
			);
			return;
		}
		else if(loadError == ERR_SUP_FILINV){
			printf(
				"\n\tError: File is not valid.\n\n"
			);
			return;
		}
		
		else if (loadError == ERR_SUP_PROGSZ){
			printf(
				"\n\tError: Program too big for region.\n\n"
			);
			return;
		}
		else if (loadError == ERR_SUP_LDADDR){
			printf(
					"\n\tError: Invalid load address.\n\n"
				);
				return;
		}
		else if (loadError == ERR_SUP_LDFAIL){
			printf(
				"\n\tError: Load Failed.\n\n"
			);
			return;
		}
	}
	else if(errorState == ERR_SUP_NAMLNG){
		printf(
			"\n\tError: The pathname is too long.\n\n"
		);
		return;
	}
	else if(errorState == ERR_SUP_FILNFD){
		printf(
			"\n\tError: File not found.\n\n"
		);
		return;
	}
	else if(errorState == ERR_SUP_FILINV){
		printf(
			"\n\tError: File is not valid.\n\n"
		);
		return;
	}
}

/* Deletes a process.
 */
void Terminate(int argc, char **argv) {
	int  error_code;
	int  error_len;
	ppcb target = NULL;
	ppq  queue  = NULL;
	
	/* If the number of arguments passed is not one, then there is a problem.
	 */
	if (argc != 1) {
		error_len = 52;
		sys_req(
			WRITE,
			TERMINAL,
			"\n\tError: Incorrect syntax.  Try \"help Terminate\".\n\n",
			&error_len
		);
		return;
	}
	
	/* If the arguments are invalid beyond the argument count, there must be
	 * something wrong with the calling code.
	 */
	if (
		(error_code = pq_find(
			&ready,
			argc,
			(void **) argv,
			(void **) &target
		)) == ERR_PQUEUE_NULLARG
	) {
		error_len = 60;
		sys_req(
			WRITE,
			TERMINAL,
			"\n\tFATAL ERROR: Terminate(int, char **) improperly called.\n\n",
			&error_len
		);
		sys_exit();
	}
	
	/* Determine which queue (if any) contains the requested process.
	 */
	if (error_code == OK) {
		queue = &ready;
	} else if (
		(error_code = pq_find(
			&blocked,
			argc,
			(void **) argv,
			(void **) &target
		)) == OK
	) {
		queue = &blocked;
	} else if (
		(error_code = pq_find(
			&suspended,
			argc,
			(void **) argv,
			(void **) &target
		)) == OK
	) {
		queue = &suspended;
	} else if (
		(error_code = pq_find(
			&suspended_blocked,
			argc,
			(void **) argv,
			(void **) &target
		)) == OK
	) {
		queue = &suspended_blocked;
	} else if (error_code == ERR_PQUEUE_ITEMNOTFOUND) {
		error_len = 49;
		sys_req(
			WRITE,
			TERMINAL,
			"\n\tError: Could not find the requested process.\n\n",
			&error_len
		);
		return;
	}
	
	/* If the process was found, remove it from its queue, and free its memory.
	 */
	pq_remove(queue, (void *) target);
	delete_data((void *) target);
}
